/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable no-case-declarations */
import type { PDFDocumentProxy } from 'pdfjs-dist';
import type { RefProxy } from 'pdfjs-dist/types/src/display/api';
import type { AnnotationEventPayload } from '../types';

interface PopupArgs {
	[key: string]: string;
}

interface LinkAnnotation {
	dest: Array<any> | string;
	url: string;
	unsafeurl: string;
}

const INTERNAL_LINK = 'internal-link';
const LINK = 'link';
const FILE_ATTACHMENT = 'file-attachment';
const FORM_TEXT = 'form-text';
const FORM_SELECT = 'form-select';
const FORM_CHECKBOX = 'form-checkbox';
const FORM_RADIO = 'form-radio';
const FORM_BUTTON = 'form-button';

const EVENTS_TO_HANDLER = ['click', 'dblclick', 'mouseover', 'input', 'change'];

function getAnnotationsByKey(key: string, value: any, annotations: Object[]): any[] {
	const result = [];
	if (annotations) {
		for (const annotation of annotations) {
			type Key = keyof typeof annotation;
			if (annotation[key as Key] === value) result.push(annotation);
		}
	}
	return result;
}

function buildAnnotationData(type: string, data: any): AnnotationEventPayload {
	return { type, data };
}

function inputAnnotation(inputEl: any, args?: any) {
	switch (inputEl.type) {
		case 'textarea':
		case 'text':
			return buildAnnotationData(FORM_TEXT, {
				fieldName: inputEl.name,
				value: inputEl.value,
			});
		case 'select-one':
		case 'select-multiple':
			const options = [];
			for (const opt of inputEl.options) {
				options.push({
					value: opt.value,
					label: opt.label,
				});
			}
			const selected = [];
			for (const opt of inputEl.selectedOptions) {
				selected.push({
					value: opt.value,
					label: opt.label,
				});
			}
			return buildAnnotationData(FORM_SELECT, {
				fieldName: inputEl.name,
				value: selected,
				options,
			});
		case 'checkbox':
			return buildAnnotationData(FORM_CHECKBOX, {
				fieldName: inputEl.name,
				checked: inputEl.checked,
			});
		case 'radio':
			return buildAnnotationData(FORM_RADIO, {
				fieldName: inputEl.name,
				...args,
			});
		case 'button':
			return buildAnnotationData(FORM_BUTTON, {
				fieldName: inputEl.name,
				...args,
			});
	}
}

function fileAnnotation(annotation: any) {
	return buildAnnotationData(FILE_ATTACHMENT, annotation.file);
}

async function linkAnnotation(
	annotation: {
		dest?: any;
		url?: string;
		unsafeUrl?: string;
	},
	PDFDoc: PDFDocumentProxy
) {
	if (annotation.dest) {
		let explicitDest;
		if (typeof annotation.dest === 'string') explicitDest = await PDFDoc.getDestination(annotation.dest);
		else explicitDest = annotation.dest;

		if (!Array.isArray(explicitDest)) {
			console.warn(`Destination "${explicitDest}" is not a valid destination (dest="${annotation.dest}")`);
			return buildAnnotationData(INTERNAL_LINK, {
				referencedPage: null,
				offset: null,
			});
		}

		let offset = null;
		if (explicitDest.length === 5) {
			offset = {
				left: annotation.dest[2],
				bottom: annotation.dest[3],
			};
		}

		const [destRef] = explicitDest;
		if (Number.isInteger(destRef)) {
			return buildAnnotationData(INTERNAL_LINK, {
				referencedPage: Number(destRef) + 1,
				offset,
			});
		} else if (typeof destRef === 'object') {
			const pageNumber = await PDFDoc.getPageIndex(destRef as RefProxy);
			return buildAnnotationData(INTERNAL_LINK, {
				referencedPage: pageNumber + 1,
				offset,
			});
		} else {
			console.warn(`Destination "${destRef}" is not a valid destination (dest="${annotation.dest}")`);
			return buildAnnotationData(INTERNAL_LINK, {
				referencedPage: null,
				offset: null,
			});
		}
	} else if (annotation.url) {
		return buildAnnotationData(LINK, {
			url: annotation.url,
			unsafeUrl: annotation.unsafeUrl,
		});
	}
}

function mergePopupArgs(annotation: HTMLElement) {
	for (const spanElement of annotation.getElementsByTagName('span')) {
		let content = spanElement.textContent;
		const args = JSON.parse(spanElement.dataset.l10nArgs ?? '{}') as PopupArgs;
		if (content) {
			for (const key in args) content = content.replace(`{{${key}}}`, args[key]);
		}
		spanElement.textContent = content;
	}
}

// Use this function to handle annotation events
function annotationEventsHandler(evt: Event, PDFDoc: PDFDocumentProxy, Annotations: Object[]) {
	let annotation = (evt.target as HTMLInputElement).parentNode! as HTMLElement;

	// annotations are <section> elements if div returned find in child nodes the section element
	if (annotation.tagName === 'DIV') annotation = annotation.firstChild! as HTMLElement;

	if (annotation.className === 'linkAnnotation' && evt.type === 'click') {
		const id: string | undefined = annotation.dataset?.annotationId;
		if (id) return linkAnnotation(getAnnotationsByKey('id', id, Annotations)[0] as LinkAnnotation, PDFDoc);
	} else if (annotation.className.includes('popupAnnotation') || annotation.className.includes('textAnnotation')) {
		mergePopupArgs(annotation);
	} else if (annotation.className.includes('fileAttachmentAnnotation')) {
		mergePopupArgs(annotation);
		const id = annotation.dataset.annotationId;
		if (id && evt.type === 'dblclick') return fileAnnotation(getAnnotationsByKey('id', id, Annotations)[0]);
	} else if (annotation.className.includes('textWidgetAnnotation') && evt.type === 'input') {
		let inputElement: HTMLInputElement | HTMLTextAreaElement = annotation.getElementsByTagName('input')[0];
		if (!inputElement) inputElement = annotation.getElementsByTagName('textarea')[0];
		return inputAnnotation(inputElement);
	} else if (annotation.className.includes('choiceWidgetAnnotation') && evt.type === 'input') {
		return inputAnnotation(annotation.getElementsByTagName('select')[0]);
	} else if (annotation.className.includes('buttonWidgetAnnotation checkBox') && evt.type === 'change') {
		return inputAnnotation(annotation.getElementsByTagName('input')[0]);
	} else if (annotation.className.includes('buttonWidgetAnnotation radioButton') && evt.type === 'change') {
		const id = annotation.dataset.annotationId;
		if (id) {
			const anno = getAnnotationsByKey('id', id, Annotations)[0];
			const radioOptions = [];
			for (const radioAnnotations of getAnnotationsByKey('fieldName', anno.fieldName, Annotations)) {
				if (radioAnnotations.buttonValue) radioOptions.push(radioAnnotations.buttonValue);
			}
			return inputAnnotation(annotation.getElementsByTagName('input')[0], {
				value: anno.buttonValue,
				defaultValue: anno.fieldValue,
				options: radioOptions,
			});
		}
	} else if (annotation.className.includes('buttonWidgetAnnotation pushButton') && evt.type === 'click') {
		const id = annotation.dataset.annotationId;
		if (id) {
			const anno = getAnnotationsByKey('id', id, Annotations)[0];
			if (!anno.resetForm) {
				return inputAnnotation({ name: anno.fieldName, type: 'button' }, { actions: anno.actions, reset: false });
			} else {
				return inputAnnotation({ name: anno.fieldName, type: 'button' }, { actions: anno.actions, reset: true });
			}
		}
	}
}

export { annotationEventsHandler, EVENTS_TO_HANDLER };
